/**
 * 文件名：SerialPortListener.java
 *
 * 创建人：Elvin Zhao - 279152260@qq.com
 *
 * 创建时间：2018年7月18日 下午6:20:29
 *
 * 版权所有：FNII
 */
package com.springvision.core.serialport;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.TooManyListenersException;

import gnu.io.CommPortIdentifier;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;

/**
 * [描述信息：说明类的基本功能]
 *
 * @author Elvin Zhao - 279152260@qq.com
 * @version 1.0 Created on 2018年7月18日 下午6:20:29
 */
public abstract class AbstractSerialPortListener implements SerialPortEventListener {

	protected static Integer bps = 9600;

	protected static int TIMEOUT = 2000;

	protected CommPortIdentifier port;

	protected SerialPort serialPort;

	protected InputStream is;

	protected OutputStream os;

	public AbstractSerialPortListener() {
		super();
	}

	protected abstract void onReceive();

	public void initAndOpen(CommPortIdentifier port)
			throws IOException, UnsupportedCommOperationException, TooManyListenersException, PortInUseException {
		/*
		 * open方法打开通讯端口，获得一个CommPort对象。它使程序独占端口。如果端口正被其他应用程序占用，将使用
		 * CommPortOwnershipListener事件机制，传递一个PORT_OWNERSHIP_REQUESTED事件。 每个端口都关联一个 InputStream
		 * 何一个OutputStream。如果端口是用open方法打开的，那么任何的getInputStream都将返回相同的数据流对象， 除非有close
		 * 被调用。有两个参数，第一个为应用程序名；第二个参数是在端口打开时阻塞等待的毫秒数。
		 */
		serialPort = (SerialPort) port.open(port.getName(), TIMEOUT);

		/* 注册一个SerialPortEventListener事件来监听串口事件 */
		serialPort.addEventListener(this);
		// 设置监听模式为当有数据到达时唤醒监听线程。
		serialPort.notifyOnDataAvailable(true);

		// TODO
		// System.out.println("设置串口初始化参数。。。（波特率9600，数据位8，停止位1，无奇偶校验）");
		serialPort.setSerialPortParams(bps, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

		os = serialPort.getOutputStream();
		/* 获取端口的输入流对象 */
		is = serialPort.getInputStream();
	}

	public void initAndOpen()
			throws IOException, UnsupportedCommOperationException, TooManyListenersException, PortInUseException {
		/*
		 * open方法打开通讯端口，获得一个CommPort对象。它使程序独占端口。如果端口正被其他应用程序占用，将使用
		 * CommPortOwnershipListener事件机制，传递一个PORT_OWNERSHIP_REQUESTED事件。 每个端口都关联一个 InputStream
		 * 何一个OutputStream。如果端口是用open方法打开的，那么任何的getInputStream都将返回相同的数据流对象， 除非有close
		 * 被调用。有两个参数，第一个为应用程序名；第二个参数是在端口打开时阻塞等待的毫秒数。
		 */
		serialPort = (SerialPort) port.open(port.getName(), TIMEOUT);

		/* 注册一个SerialPortEventListener事件来监听串口事件 */
		serialPort.addEventListener(this);
		// 设置监听模式为当有数据到达时唤醒监听线程。
		serialPort.notifyOnDataAvailable(true);

		// TODO
		// System.out.println("设置串口初始化参数。。。（波特率9600，数据位8，停止位1，无奇偶校验）");
		serialPort.setSerialPortParams(bps, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

		os = serialPort.getOutputStream();
		/* 获取端口的输入流对象 */
		is = serialPort.getInputStream();
	}

	public void close() {
		System.out.println("串口：关闭。。。");
		if (os != null)
			try {
				os.close();
			} catch (IOException e) {
			}
		if (is != null)
			try {
				is.close();
			} catch (IOException e) {
			}
		if (serialPort != null)
			serialPort.close();
	}

	public void send(byte[] arr) throws IOException {
		if (arr != null && os != null) {
			System.out.println("串口：发送字节个数" + arr.length);
			os.write(arr);
		} else {
			System.out.println("串口：发送失败，发送数据为空或没打开串口");
		}
	}

	// // 读取串口返回信息
	// protected void onReceive() {
	// ByteArrayOutputStream baos = new ByteArrayOutputStream();
	// try {
	// // TODO 修改部分，供参考
	// byte[] readBuffer = new byte[1024];
	// int len = 0;
	// String test = "";
	// while ((len = is.read(readBuffer)) != -1) {
	// // System.out.println("实时反馈：" + new String(readBuffer, 0,
	// // len).trim() + new Date());, "gbk"
	// test += new String(readBuffer, 0, len).trim();
	// break;
	// }
	// SerialPortProtocol serialPortProtocol = new SerialPortProtocol(test);
	// System.out.println(serialPortProtocol.getDevice() + "\t" + serialPortProtocol.getValue());
	//
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	//
	// }

	/**
	 * 串口事件 实现接口SerialPortEventListener中的方法 读取从串口中接收的数据，处理监控到的串口事件
	 * 
	 * @see gnu.io.SerialPortEventListener#serialEvent(gnu.io.SerialPortEvent)
	 * @author: Elvin Zhao - 279152260@qq.com 
	 * @date: Created on 2018年7月18日 下午6:07:45
	 */
	@Override
	public void serialEvent(SerialPortEvent event) {

//		System.err.println("========================>>>SerialPortEvent :" + event.getEventType());
		switch (event.getEventType()) {
			case SerialPortEvent.BI:// 通讯中断 10
			case SerialPortEvent.OE:// 溢位错误 7
			case SerialPortEvent.FE:// 传帧错误 9
			case SerialPortEvent.PE:// 奇偶校验错误 8
			case SerialPortEvent.CD:// 载波检测 6
			case SerialPortEvent.CTS:// 清除发送 3
			case SerialPortEvent.DSR:// 数据设备准备好 4
			case SerialPortEvent.RI:// 响铃侦测 5
			case SerialPortEvent.OUTPUT_BUFFER_EMPTY:// 输出缓冲区已清空 2
				break;
			case SerialPortEvent.DATA_AVAILABLE:// 串口存在可用数据 1
				onReceive();
				break;
			default:
				break;
		}
	}

	public static void main(String[] args) throws Exception {
		// COM1串口发送数据，在COM2--串口调试工具 接收数据
		// 监听串口COM2读
		CommPortIdentifier port = SerialPortUtils.getSerialPort("COM2");
		System.out.println(port.getName());
		// SerialPortClient client = new SerialPortClient(port);
		// client.initAndOpen();

		// client.close();

	}
}
